package com.cellulam.framework.scheduled;

import com.cellulam.framework.core.utils.JacksonUtils;
import com.cellulam.framework.scheduled.exceptions.TaskDelayException;
import com.cellulam.framework.scheduled.exceptions.TaskExpireException;
import com.cellulam.framework.scheduled.exceptions.TaskTerminateException;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Slf4j
public abstract class AbstractTaskProcessor<PARAM> implements TaskProcessor {

    private static final ExecutorService EVENT_POOL = Executors.newCachedThreadPool();

    @Override
    public final ExecuteResult run(ScheduleTask task) throws TaskDelayException, TaskTerminateException, TaskExpireException {

        if (task.getScheduleExpireTime() != null
                && LocalDateTime.now().isAfter(task.getScheduleExpireTime())) {
            throw new TaskExpireException("任务%s超时: %s", task.getId(), task.getScheduleExpireTime());
        }
        return this.preProcess(task, this.analysisParam(task, task.getParam()));
    }

    private ExecuteResult preProcess(ScheduleTask task, PARAM param) throws TaskTerminateException, TaskDelayException, TaskExpireException {
        try {
            return this.process(task, param);
        } catch (ClassCastException e) {
            log.error("解析参数异常{}", task.toString(), e);
            throw new TaskTerminateException("解析参数异常");
        }
    }

    @Override
    public final void processException(ScheduleTask task, Exception e) {
        this.executeAsync(() -> this.taskException(task, e));
    }


    @Override
    public final void processComplete(ScheduleTask task, ExecuteResult result) {
        this.executeAsync(() -> this.taskProcessed(task, result));
    }

    @Override
    public final void processExpire(ScheduleTask task) {
        this.executeAsync(() -> this.taskExpired(task));
    }

    @Override
    public final void processBegin(ScheduleTask task) {
        this.taskBefore(task);
    }

    protected void executeAsync(Runnable runnable) {
        EVENT_POOL.submit(() -> {
            try {
                runnable.run();
            } catch (Exception e) {
                log.error("", e);
            }
        });
    }

    /**
     * 任务处理异常
     *
     * @param task
     * @param e
     */
    protected void taskException(ScheduleTask task, Exception e) {
        log.error("任务处理异常{}", task.toString(), e);
    }

    /**
     * 任务开始处理
     *
     * @param task
     */
    protected void taskBefore(ScheduleTask task) {
        log.info("任务开始处理{}", task.toString());
    }

    /**
     * 任务过期
     *
     * @param task
     */
    protected void taskExpired(ScheduleTask task) {
        log.info("任务已过期{}", task.toString());
    }

    /**
     * 任务处理完成
     *
     * @param task
     * @param result
     */
    protected void taskProcessed(ScheduleTask task, ExecuteResult result) {
        log.info("任务处理完成{}, 结果: {}", task.toString(), JacksonUtils.toJson(result));
    }

    /**
     * 解析参数，默认json反序列化
     *
     * @param param
     * @return
     */
    protected PARAM analysisParam(ScheduleTask task, String param) throws TaskTerminateException {
        try {
            return JacksonUtils.toObjByReference(param, new TypeReference<PARAM>() {
            });
        } catch (Exception e) {
            log.error("解析参数异常{}", task.toString(), e);
            throw new TaskTerminateException("解析参数异常");
        }
    }

    /**
     * 任务处理
     *
     * @param task
     * @param param
     * @return
     * @throws TaskDelayException
     * @throws TaskTerminateException
     * @throws TaskExpireException
     */
    protected abstract ExecuteResult process(ScheduleTask task, PARAM param) throws TaskDelayException, TaskTerminateException, TaskExpireException;
}
